home *** CD-ROM | disk | FTP | other *** search
/ Nebula 2 / Nebula Two.iso / Apps / ScreenSavers / SpaceSaver / Source / SpaceSaver.m < prev    next >
Encoding:
Text File  |  1995-06-12  |  17.4 KB  |  659 lines

  1. // ScreenSaver.m
  2. //
  3. // This class is the brains behind the SpaceSaver login bundle.
  4. //
  5. // This recast of BackSpace into a ScreenSaver bundle for use when no one
  6. // is logged in was done by Christopher_Lane@Med.Stanford.EDU, December 1993.
  7. //
  8. // The original BackSpace application was done by Sam Streeper of NeXT,
  9. // with contributions from Bill Bumgarner, Lennart Lovstrand, Bruce Blumberg,
  10. // shou-h@nexus.or.jp and others.  Many of the code comments are also Sam's.
  11. //
  12. // Undocumented 3.3 SpaceSaver.loginbundle details obtained via /bin/otool.
  13. //
  14. // You may freely copy, distribute, and reuse the code in this example.
  15. // NeXT disclaims any warranty of any kind, expressed or  implied, as
  16. // to its fitness for any particular use.
  17.  
  18. #import "SpaceSaver.h"
  19. #import "SpaceView.h"
  20. #import "psfuncts.h"
  21.  
  22. #import <sys/dir.h>
  23. #import <appkit/appkit.h>
  24.  
  25. #ifdef KLUDGE
  26. static NXDefaultsVector MovieShowDefaults = { // keep uninitialized MovieShowView from failing
  27.     {"Jump", "NO"},
  28.     {"SlideFrames", "NO"},
  29.     {"SlidePauses", "YES"},
  30.     {NULL}
  31.     };
  32.  
  33. static NXDefaultsVector MultiViewDefaults = { // keep uninitialized MulitView from failing
  34.     {"MultiLayout", "0"},
  35.     {"MultiViews", "Space"},
  36.     {NULL}
  37.     };
  38. #endif
  39.  
  40. static NXDefaultsVector BackSpaceDefaults = {
  41.     {"ViewDirectory", "/LocalLibrary/BackSpaceViews"},
  42.     {"viewType", "All"},
  43.     {"priority", "4"},
  44.     {NULL}
  45.     };
  46.  
  47. static char *compiledViewNames[] = { "Space" }, *applicationName, *launchDir;
  48.  
  49. #define COMVIEWCOUNT (sizeof(compiledViewNames) / sizeof(*compiledViewNames))
  50.  
  51. typedef enum {MINIMUMPRIORITY = 0, BACKSPACEPRIORITY = 4, DEFAULTPRIORITY = 16, MAXIMUMPRIORITY = 31} PRIORITY;
  52.  
  53. #define streq(s, t) (strcmp(s, t) == 0)
  54.  
  55. #define MSECPERSEC (1000)
  56. #define USECPERMSEC (1000)
  57.  
  58. static id _BSThinker, appDelegate;
  59. id BSThinker() { return _BSThinker; }
  60.  
  61. static int eventMask, basePriority;
  62. BOOL doesDidLockFocus;
  63.  
  64. #define BACKSPACENAME "BackSpace"
  65.  
  66. #define DEFAULTIMAGENAME "defaultImage"
  67.  
  68. #define BINARYEXTENSION "BackO"
  69. #define BUNDLEEXTENSION "BackModule"
  70.  
  71. @implementation SpaceSaver
  72.  
  73. + alloc
  74. {
  75.     static id instance; // probably not a good idea to have more than one of these...
  76.  
  77.     if (instance == nil) instance = [super alloc];
  78.  
  79.     return (self = instance);
  80. }
  81.  
  82. - didStartScreenSaver
  83. {
  84.     if (screenSaverVal) return self;
  85.  
  86.     if (!doingSaver) [self createScreenSaver];
  87.  
  88.     [self setScreenSaver:YES];
  89. #ifndef DEBUG
  90.     PShidecursor();
  91.     [self blackOutAllScreens];
  92. #endif
  93.     [self setVirtualViewIndex];
  94.  
  95.     [[spaceView fillBoundsWithBlack] display];
  96.     [[spaceWindow display] makeKeyAndOrderFront:self];
  97.  
  98. #ifndef DEBUG
  99.     (void) [spaceWindow addToEventMask:NX_MOUSEMOVEDMASK];
  100. #endif
  101.     eventMask = [spaceWindow eventMask];
  102.  
  103.     NXPing();
  104.  
  105.     if (priority != basePriority) cthread_priority(cthread_self(), priority, FALSE);
  106.  
  107.     if ([spaceView respondsTo:@selector(enteredScreenSaverMode)]) [spaceView enteredScreenSaverMode];
  108.  
  109.     return self;
  110. }
  111.  
  112. - didStopScreenSaver
  113. {
  114.     if (!screenSaverVal) return self;
  115.  
  116.     [self setScreenSaver:NO];
  117.  
  118.     if ([spaceView respondsTo:@selector(willExitScreenSaverMode)]) [spaceView willExitScreenSaverMode];
  119.  
  120.     if (basePriority != priority) cthread_priority(cthread_self(), basePriority, FALSE);
  121.  
  122.     [spaceWindow orderOut:self];
  123.  
  124.     (void) [spaceWindow setEventMask:eventMask]; // loaded view may have changed event mask on us.
  125. #ifndef DEBUG
  126.     (void) [spaceWindow removeFromEventMask:NX_MOUSEMOVEDMASK];
  127.     [self unBlackOutAllScreens];
  128.     PSshowcursor();
  129. #endif
  130.     return self;
  131. }
  132.  
  133. - oneStep
  134. {
  135. //    NXEvent dummyEvent;
  136.  
  137.     if (!screenSaverVal) return nil; // timed entry misfire
  138.  
  139.     [spaceView lockFocus]; {
  140.         if (doesDidLockFocus) [spaceView didLockFocus];
  141. //        do {
  142.             [spaceView oneStep];
  143.             [spaceWindow flushWindow];
  144.             NXPing(); // Synchronize postscript for smoother animation
  145. //            } while (screenSaverVal && ([NXApp peekNextEvent:NX_ALLEVENTS into:&dummyEvent waitFor:0 threshold:NX_BASETHRESHOLD] == NULL));
  146.         } [spaceView unlockFocus];
  147.  
  148.     return self;
  149. }
  150.  
  151. - createScreenSaver
  152. {
  153.     const char *string;
  154.     char buffer[MAXPATHLEN];
  155.  
  156.     NXRegisterDefaults(BACKSPACENAME, BackSpaceDefaults);
  157. #ifdef KLUDGE
  158.     (void) sprintf(buffer, "%s/defaultImage.tiff", [[NXBundle bundleForClass:[self class]] directory]);
  159.     (void) NXSetDefault(BACKSPACENAME, "imageFile", buffer);
  160.     [self borrowDefaults:BACKSPACENAME]; // if module does NXGetDefaultValue([NXApp appName], ...
  161.     NXRegisterDefaults(applicationName, MultiViewDefaults);
  162. #endif
  163.     launchDir = NXCopyStringBuffer([[NXBundle mainBundle] directory]);
  164.     
  165.     [NXApp getScreens:&screens count:&screenCount];
  166.  
  167.     [self getViewType];
  168.  
  169.     [self getPrioritySetting];
  170.  
  171.     [self getImageFile];
  172.  
  173.     if ((string = NXGetDefaultValue(BACKSPACENAME, "viewType")) != NULL) realViewIndex = [moduleList indexOfName:string];
  174.  
  175.     doingSaver = YES;
  176.  
  177.     return self;
  178. }
  179.  
  180. - init // do the easy stuff, leave the rest for first time we 'start'
  181. {
  182.  
  183.     _BSThinker = self;
  184.  
  185.     screenSaverVal = doingSaver = NO;
  186.  
  187.     backZone = NXCreateZone(vm_page_size, vm_page_size, YES);
  188.  
  189.     moduleList = [[ModuleList alloc] init];
  190.  
  191.     basePriority = priority = DEFAULTPRIORITY;
  192.  
  193.     realViewIndex = virtualViewIndex = -1;
  194.  
  195.     currentInspector = commonImageInspector = nullInspector = nil;
  196.  
  197.     appDelegate = [NXApp delegate];
  198.  
  199.     applicationName = (char *) [NXApp appName];
  200. #ifndef DEBUG
  201.     srandom(time(NULL));
  202. #endif
  203. #ifdef KLUDGE
  204.     (void) fmod(2.0, 3.0); // force 'fmod' to not inline and let SpewView load
  205. #endif
  206.     return self;
  207. }
  208.  
  209. - free
  210. {
  211.     [self didStopScreenSaver];
  212.  
  213.     return [super free];
  214. }
  215.  
  216. int readstr(FILE *stream, char *s) // wish I could use scanf but %s won't read unanticipated "xxx yyy"
  217. {
  218.     int c, flag = FALSE;
  219.  
  220.     while ((c = getc(stream)) != EOF) {
  221.         if (c == '"') flag = !flag;
  222.         else if ((c == ' ' || c == '\n') && !flag) break;
  223.         *s++ = c;
  224.         }
  225.     *s = '\0';
  226.  
  227.     return c;
  228. }
  229.  
  230. - borrowDefaults:(const char *) realOwner
  231. { // NeXT should have defined (NXDefaultsVector *) NXReadDefaults(const char *owner)
  232.     FILE *pipe;
  233.     char string[MAXPATHLEN];
  234.  
  235.     (void) sprintf(string, "dread -o %s", realOwner);
  236.  
  237.     if ((pipe = popen(string, "r")) != NULL) {
  238.         char owner[MAXNAMLEN], name[MAXNAMLEN], value[MAXPATHLEN];
  239.  
  240.         while (readstr(pipe, owner) != EOF && readstr(pipe, name) != EOF && readstr(pipe, value) != EOF) {
  241.             if (streq(owner, realOwner) && NXGetDefaultValue(applicationName, name) == NULL) {
  242.                 (void) NXSetDefault(applicationName, name, value); // loginwindow & BackSpace overlap!
  243.                 }
  244. #ifdef DEBUG
  245.             else (void) fprintf(stderr, "%s %s %s\n", owner, name, value);
  246. #endif
  247.             }
  248.         (void) pclose(pipe);
  249.         }
  250.     else return nil;
  251.  
  252.     return self;
  253. }
  254.  
  255. - (NXZone *) backZone { return backZone; }
  256.  
  257. - (ModuleList *) moduleList { return moduleList; }
  258.  
  259. - installSpaceViewIntoWindow
  260. {
  261.     NXRect cvrect;
  262.     unsigned int i, count;
  263.     id subviews, contentView = [spaceWindow contentView];
  264.  
  265.     [contentView getBounds:&cvrect];
  266.  
  267.     subviews = [contentView subviews]; // remove old subviews, this is overkill really...
  268.     for (i = 0, count = [subviews count]; i < count; i++) [[subviews objectAt:i] removeFromSuperview];
  269.  
  270.     (void) [contentView addSubview:spaceView];
  271.  
  272.     [contentView setAutoresizeSubviews:YES]; // don't really need to resize but some views break if you don't!
  273.  
  274.     [spaceView setAutosizing:NX_WIDTHSIZABLE | NX_HEIGHTSIZABLE];
  275.  
  276.     [spaceView sizeTo:cvrect.size.width :cvrect.size.height];
  277.  
  278.     return self;
  279. }
  280.  
  281. - (int) backingTypeForView:aView
  282. {
  283.     if ([aView respondsTo:@selector(useBufferedWindow)] && [aView useBufferedWindow]) return NX_BUFFERED;
  284.  
  285.     return NX_RETAINED;
  286. }
  287.  
  288. - createBigWindowIfNecessaryForBacking:(int) backing
  289. {
  290.     id window = nil;
  291. #ifndef DEBUG
  292.     const NXScreen *mainScreen = [NXApp mainScreen];
  293.     NXRect r = mainScreen->screenBounds;
  294. #else
  295.     NXRect r = {{0, 0}, {640, 480}};
  296. #endif
  297.     if ((backing == NX_RETAINED) && !bigUnbufferedWindow) {
  298.         window = bigUnbufferedWindow = [[Window allocFromZone:backZone]
  299.             initContent:&r style:NX_TOKENSTYLE backing:NX_NONRETAINED buttonMask:0 defer:NO];
  300.         }
  301.  
  302.     if ((backing == NX_BUFFERED) && !bigBufferedWindow) {
  303.         window = bigBufferedWindow = [[Window allocFromZone:backZone]
  304.             initContent:&r style:NX_TOKENSTYLE backing:NX_BUFFERED buttonMask:0 defer:NO];
  305.         [[bigBufferedWindow setOneShot:YES] setDynamicDepthLimit:YES]; // want window depth to match device!
  306.         }
  307.  
  308.     if (window != nil) {
  309.         [[window useOptimizedDrawing:YES] setBackgroundGray:NX_BLACK];
  310.         tweakWindow([window windowNum], SAVERTIER);
  311.         }
  312.  
  313.     return self;
  314. }
  315.  
  316. - setScreenSaver:(BOOL) val
  317. {
  318.     screenSaverVal = val;
  319.  
  320.     // I don't handle any app* messages but some modules assume I do,
  321.  
  322.     if (screenSaverVal) [NXApp setDelegate:self];
  323.     else [NXApp setDelegate:appDelegate];
  324.  
  325.     return self;
  326. }
  327.  
  328. - getPrioritySetting
  329. {
  330.     struct thread_basic_info info;
  331.     int value, count = THREAD_BASIC_INFO_COUNT;
  332.     const char *string = NXGetDefaultValue(BACKSPACENAME, "priority");
  333.  
  334.     if(thread_info(cthread_thread(cthread_self()), THREAD_BASIC_INFO, (thread_info_t) &info, &count) == KERN_SUCCESS) {
  335.         basePriority = info.base_priority;
  336.         }
  337.  
  338.     if (sscanf(string, "%d", &value) == 1 && value >= MINIMUMPRIORITY && value <= MAXIMUMPRIORITY) priority = value;
  339.     else priority = BACKSPACEPRIORITY;
  340.  
  341.     return self;
  342. }
  343.  
  344. BStimeval currentTimeInMs()
  345. {
  346.     struct timeval curTime;
  347.  
  348.     gettimeofday(&curTime, NULL);
  349.  
  350.     return ((curTime.tv_sec * MSECPERSEC) + (curTime.tv_usec / USECPERMSEC));
  351. }
  352.  
  353. // Additional methods to handle a common image object for views.
  354. // Lennart Lovstrad, Rank Xerox EuroPARC, August 1991.
  355.  
  356. - setImageFromName:(const char *) name
  357. {
  358.     if (image != nil) [image free];
  359.  
  360.     image = [[NXImage allocFromZone:backZone] initFromSection:name];
  361.  
  362.     return [self commonImageInit];
  363. }
  364.  
  365. - setImageFromFile:(const char *) filename
  366. {
  367.     if (image != nil) [image free];
  368.  
  369.     image = [[NXImage allocFromZone:backZone] initFromFile:filename];
  370.  
  371.     return [self commonImageInit];
  372. }
  373.  
  374. - commonImageInit
  375. {
  376.     if ([spaceView respondsTo:@selector(setImage:)]) [spaceView setImage:image];
  377.  
  378.     if ([self backingTypeForView:spaceView] != NX_BUFFERED) [[spaceView fillBoundsWithBlack] display];
  379.  
  380.     return self;
  381. }
  382.  
  383. - getImageFile
  384. {
  385.     const char *filename;
  386.  
  387.     if ((filename = NXGetDefaultValue(BACKSPACENAME, "imageFile")) != NULL) [self setImageFromFile:filename];
  388.     else [self setImageFromName:DEFAULTIMAGENAME];
  389.  
  390.     return self;
  391. }
  392.  
  393. float frandom() // This should return a float between 0 and 1
  394. {
  395.     return (((float) (random() & MAXLONG)) / (float) MAXLONG);
  396. }
  397.  
  398. float randBetween(float low, float high)
  399. {
  400.     float temp = low;
  401.  
  402.     if (low > high) { low = high; high = temp; }
  403.  
  404.     return (((high - low) * frandom()) + low);
  405. }
  406.  
  407. // float randBetween(float a, float b) { return (MIN(a, b) + (fabs(a - b) * frandom())); }
  408.  
  409. - getViewType // must invoke this before creating window or setting up the views
  410. {
  411.     unsigned int i;
  412.  
  413.     [self loadViewsFrom:NXGetDefaultValue(BACKSPACENAME, "ViewDirectory")];
  414.  
  415.     for (i = 0; i < COMVIEWCOUNT; i++) {
  416.         [moduleList addObject:[[ModuleInfo alloc] initWithView:nil name:compiledViewNames[i] path:NULL]];
  417.         }
  418.  
  419.     [moduleList sort];
  420.  
  421.     return self;
  422. }
  423.  
  424. // this method is the actual view setting mechanism,
  425. // guaranteed to get called to set the view
  426.  
  427. - setVirtualViewIndex
  428. {
  429.     if (realViewIndex != -1) virtualViewIndex = realViewIndex;
  430.     else {
  431. #ifndef DEBUG
  432.         virtualViewIndex = random() % [moduleList count];
  433. #else
  434.         if (++virtualViewIndex >= [moduleList count]) virtualViewIndex = 0;
  435. #endif
  436.         }
  437.  
  438.     while ([self selectScreenSaverViews] == nil || ([spaceView respondsTo:@selector(isBoringScreenSaver)] && [spaceView isBoringScreenSaver])) {
  439.         if (++virtualViewIndex >= [moduleList count]) virtualViewIndex = 0;
  440.         }
  441.  
  442.     return self;
  443. }
  444.  
  445. - selectScreenSaverViews
  446. {
  447.     int myBacking;
  448.  
  449.     if ((spaceView = [self backView]) == nil) return nil;
  450.  
  451.     myBacking = [self backingTypeForView:spaceView];
  452.  
  453.     [self createBigWindowIfNecessaryForBacking:myBacking];
  454.  
  455.     spaceWindow = (myBacking == NX_BUFFERED) ? bigBufferedWindow : bigUnbufferedWindow;
  456. #ifdef DEBUG
  457.     (void) fprintf(stderr, "Window type = NX_%sBUFFERED\n", (myBacking == NX_BUFFERED) ? "" : "UN");
  458. #endif
  459.     [self installSpaceViewIntoWindow];
  460. #ifdef KLUDGE
  461.     if ([spaceView respondsTo:@selector(inspector:)]) currentInspector = [spaceView inspector:self];
  462.     else currentInspector = [self nullInspector]; // don't use inspectors, but some modules use to initialize -- sigh
  463. #endif
  464.     if ([spaceView respondsTo:@selector(setImage:)]) [spaceView setImage:image];
  465.     if ([spaceView respondsTo:@selector(newWindow)]) [spaceView newWindow];
  466.     doesDidLockFocus = [spaceView respondsTo:@selector(didLockFocus)];
  467.  
  468.     return self;
  469. }
  470.  
  471. - backView
  472. {
  473.     id theView;
  474.     ModuleInfo *info = [moduleList objectAt:virtualViewIndex];
  475.  
  476.     if ((theView = [info view]) == nil) {
  477.         char class[MAXNAMLEN];
  478. #ifdef DEBUG
  479.         NXRect aFrame = {{0, 0}, {640, 480}};
  480. #else
  481.         const NXScreen *mainScreen = [NXApp mainScreen];
  482.         NXRect aFrame = mainScreen->screenBounds;
  483. #endif
  484.         (void) sprintf(class, "%sView", [info viewName]);
  485.  
  486.         // before I loaded all classes at launch time; now classes are loaded only as
  487.         // needed.  This idea and some of the code here is from bill bumgarner, thanx!
  488.  
  489.         if ([info path]) { // we have path but no instance, must load class
  490.             char path[MAXPATHLEN]; char *filenames[] = {path, NULL}; // order dependency
  491.             long result;
  492.             struct mach_header *header;
  493.             NXStream *stream = NULL;
  494. #ifdef DEBUG
  495.             char *address;
  496.             int length, maximum;
  497. #endif
  498.             do {
  499. #ifdef DEBUG
  500.                 stream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
  501. #endif
  502.                 (void) sprintf(path, "%s/%s.%s", [info path], class, BINARYEXTENSION);
  503.                 result = objc_loadModules(filenames, stream, NULL, &header, NULL);
  504. #ifdef DEBUG
  505.                 NXFlush(stream);
  506.                 NXGetMemoryBuffer(stream, &address, &length, &maximum);
  507.                 (void) fprintf(stderr, address);
  508.                 NXCloseMemory(stream, NX_FREEBUFFER);
  509. #endif
  510.                 // objc_loadModules succeeds with a warning if the architecture of the
  511.                 // object file is wrong, so we better check if we really got a class
  512.  
  513.                 if (result == 0 && objc_getClass(class) == nil) result = -1;
  514.  
  515.                 } while (result != 0 && [info useNextPath] != nil);
  516.  
  517.             [info discardAltPaths];
  518.  
  519. #ifdef DEBUG
  520.             (void) fprintf(stderr, "Dynamic load of class: %s -- %s!\n", class, (result == 0) ? "succeeded" : "failed");
  521. #endif
  522.             if (result != 0) return nil; // Ugh, failed -- return failure.
  523.             else [info setHeader:header];
  524.             }
  525.  
  526.         theView = [objc_getClass(class) allocFromZone:backZone]; // at this point we must have a valid name for a loaded class
  527. #ifdef KLUDGE
  528.         if (streq(class, "MovieShowView")) { // don't prompt user for move name
  529.             char buffer[MAXPATHLEN];
  530.     
  531.             NXRegisterDefaults("MovieShow", MovieShowDefaults);
  532.  
  533.             (void) sprintf(buffer, "%s/defaultImage.anim", [[NXBundle bundleForClass:[self class]] directory]);
  534.             (void) NXSetDefault("MovieShow", "Movie", buffer);
  535.             }
  536. #endif
  537.         [info setView:[theView initFrame:&aFrame]];
  538.         }
  539.  
  540.     return theView;
  541. }
  542.  
  543. // Dynamically load all object files found in the specified directory
  544. // if we find a module in several places, we save the additional paths
  545. // in case they point to modules for different architectures
  546.  
  547. - loadViewsFrom: (const char *) dirname
  548. {
  549.     DIR *dir;
  550.     int index;
  551.     struct direct *de;
  552.     BOOL validName, filePackage;
  553.     char *iptr, name[MAXNAMLEN], path[MAXPATHLEN];
  554.  
  555.     if ((dir = opendir(dirname)) == NULL) return nil;
  556.  
  557.     while ((de = readdir(dir)) != NULL) {
  558.         if (de->d_name[0] == '.') continue; // Ignore '.'-files (not really necessary, I guess)
  559.  
  560.         filePackage = validName = NO;
  561.  
  562.         if (de->d_namlen > (strlen(BINARYEXTENSION) + 1) && streq(&de->d_name[de->d_namlen - (strlen(BINARYEXTENSION) + 1)], "." BINARYEXTENSION))
  563.             validName = YES;
  564.         else if (de->d_namlen > (strlen(BUNDLEEXTENSION) + 1) && streq(&de->d_name[de->d_namlen - (strlen(BUNDLEEXTENSION) + 1)], "." BUNDLEEXTENSION))
  565.             validName = filePackage = YES;
  566.  
  567.         if (!validName) continue;
  568.  
  569.         if (filePackage) (void) sprintf(path, "%s/%s", dirname, de->d_name);
  570.         else (void) strcpy(path, dirname);
  571.  
  572.         if ((iptr = rindex(strcpy(name, de->d_name), 'V')) != NULL) *iptr = '\0'; // Smash out the 'V' in "FooView.BackO"
  573.  
  574.         if ((index = [moduleList indexOfName:name]) != -1) {
  575.             [[moduleList objectAt:index] appendPath:path];
  576.             continue;
  577.             }
  578.  
  579.         // I used to load the class at this time; this got horribly inefficient.
  580.         // I now wait until I'm about to instantiate a view before doing this (thanx bbum!)
  581.  
  582.         [moduleList addObject:[[ModuleInfo alloc] initWithView:NULL name:name path:path]];
  583.         }
  584.  
  585.     closedir(dir);
  586.  
  587.     return self;
  588. }
  589.  
  590. - (const char *) appDirectory { return launchDir; }
  591.  
  592. - (const char *) moduleDirectory:(const char *) name
  593. {
  594.     int index = [moduleList indexOfName:name];
  595.  
  596.     if (index == -1) return NULL;
  597.  
  598.     return [[moduleList objectAt:index] path];
  599. }
  600.  
  601. - (struct mach_header *) headerForModule:(const char *) name
  602. {
  603.     int index = [moduleList indexOfName:name];
  604.  
  605.     if (index == -1) return NULL;
  606.  
  607.     return [[moduleList objectAt:index] header];
  608. }
  609.  
  610. // In the multi-headed case, I gotta throw a black window over all
  611. // the screens so they don't burn in while I do animation on one.
  612. // You'd want to black out all screen in every case if you switched
  613. // animations on the fly to prevent the screen from possibly being
  614. // unlocked for a moment.
  615.  
  616. // Hmm, I don't know why I didn't just put a single big non retained
  617. // window over all screens instead...
  618.  
  619. - blackOutAllScreens
  620. {
  621.     id window;
  622.     NXRect *rect;
  623.     unsigned int i;
  624.  
  625.     if (screenCount <= 1) return self;
  626.  
  627.     if (screenList == nil) screenList = [[List alloc] initCount:screenCount];
  628.  
  629.     for (i = 0; i < screenCount; i++) {
  630.         rect = &screens[i].screenBounds;
  631.  
  632.         window = [[Window allocFromZone:backZone] initContent:rect style:NX_TOKENSTYLE backing:NX_NONRETAINED buttonMask:0 defer:NO];
  633.  
  634.         [screenList addObject:window];
  635.  
  636.         [window setBackgroundGray:NX_BLACK];
  637.         (void) [window addToEventMask:NX_MOUSEMOVEDMASK];
  638.         tweakWindow([window windowNum], SAVERTIER - 1);
  639.         [[window placeWindowAndDisplay:rect] orderFront:self];
  640.         }
  641.  
  642.     return self;
  643. }
  644.  
  645. - unBlackOutAllScreens
  646. {
  647.     if (screenCount <= 1) return self;
  648.  
  649.     [[screenList makeObjectsPerform:@selector(orderOut:) with:self] freeObjects];
  650.  
  651.     return self;
  652. }
  653.  
  654. - nullInspector { return nullInspector; }
  655.  
  656. - commonImageInspector { return commonImageInspector; }
  657.  
  658. @end
  659.